package com.jsftoolkit.ajax;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;

import com.jsftoolkit.base.renderer.HtmlRenderer;

/**
 * The {@link AjaxContext} provides a common Server Side AJAX API for component
 * developers to write to.
 * <p>
 * What can be done on the server side of an AJAX request?
 * <ul>
 * <li>Create events. Some AJAX frameworks want to be notified when an event
 * happens, e.g. if your component performs an action, other components
 * listening on your components might be re-rendered. See
 * {@link #broadcast(FacesEvent)}.
 * <li>Choose components to re-render. Your component may be able to determine
 * that other components need to be re-rendered for the response. See
 * {@link #addComponents(UIComponent[])}. Also see
 * {@link #decode(FacesContext, UIComponent)}.
 * </ul>
 * <h2>Client Side API</h2>
 * Some AJAX frameworks operate primarily on the client side. In order to
 * accommodate both models, there is a roughly equivalent Javascript API for use
 * on the client side.
 * <p>
 * To ensure that a component will work with any framework, component authors
 * must make equivalent calls in both the client and server APIs.
 * <h2>Automatic Usage</h2>
 * Components using the JSF Toolkit base classes will automatically invoke many
 * of the hooks found in this class. In most cases, additional usage of this API
 * by the component developer is unnecessary.
 * <h2>isAjaxRequest</h2>
 * Correct usage of {@link #isAjaxRequest(FacesContext)} is important. Please
 * see the comment.
 * 
 * @author noah
 * 
 */
public abstract class AjaxContext {
	private static final ThreadLocal<AjaxContext> CURRENT = new ThreadLocal<AjaxContext>();

	/**
	 * 
	 * @return the current instance of the AjaxContext.
	 */
	public static final AjaxContext getCurrentInstance() {
		return CURRENT.get();
	}

	public static void setCurrentInstance(AjaxContext currentInstance) {
		CURRENT.set(currentInstance);
	}

	/**
	 * Convenience method.
	 * 
	 * @return isAjaxRequest(FacesContext.getCurrentInstance())
	 */
	public boolean isAjaxRequest() {
		return isAjaxRequest(FacesContext.getCurrentInstance());
	}

	/**
	 * When components use any methods on this class other than
	 * {@link #isAjaxRequest()} or {@link #isAjaxRequest(FacesContext)},
	 * implementations *may* take an otherwise normal response and turn it into
	 * an AJAX response. Therefore, components should generally wrap all calls
	 * to {@link AjaxContext} in
	 * <code>if(ajaxContext.isAjaxRequest()) { ... </code> unless the intention
	 * is to trigger an AJAX response.
	 * 
	 * @param context
	 * @return true if the request associated with the given context expects an
	 *         AJAX response.
	 */
	public abstract boolean isAjaxRequest(FacesContext context);

	/**
	 * Optional Operation. Causes the given components to be re-rendered, if
	 * supported. By default, this does nothing.
	 * <p>
	 * Component developers may use this to signal that certain components need
	 * to be re-rendered. This call must be paired with equivalent usage of the
	 * client side API to guarantee the necessary components are re-rendered.
	 * <p>
	 * As an example, the <code>DropTarget</code> component, when a draggable
	 * is dropped on it, should re-render itself and the parent component of the
	 * draggable dropped. This is accomplished by having the renderer override
	 * the decode method and adding a <code>DropListener</code>. The handler
	 * method looks like this: <code><br/>
	 * AjaxContext ajax = AjaxContext.getCurrentInstance();<br/>
	 * if (ajax.isAjaxRequest()) {<br/>
	 * &nbsp;&nbsp;ajax.addComponents(event.getTarget(), event.getDraggable().getParent());<br/>
	 * }<br/>
	 * </code>
	 * 
	 * @param components
	 *            the components to re-render. Implementations must ignore null
	 *            elements.
	 */
	public void addComponents(UIComponent... components) {
		// do nothing
	}

	/**
	 * Optional Hook. Gives the {@link AjaxContext} the opportunity to
	 * manipulate/observe a component during the decode phase. This should be
	 * sufficient for frameworks to initialize/set up/etc. components for an
	 * AJAX request.
	 * <p>
	 * The default behavior is to do nothing.
	 * <p>
	 * In general, this method should not be used by component developers. It is
	 * automatically called by
	 * {@link HtmlRenderer#decode(FacesContext, UIComponent)}.
	 * 
	 * @param context
	 * @param component
	 */
	public void decode(FacesContext context, UIComponent component) {
		// do nothing
	}

	/**
	 * Optional Hook. Notifies the AJAX Context of {@link FacesEvent}s.
	 * <p>
	 * Does nothing by default.
	 * <p>
	 * Component developers need not call this method if using a component class
	 * generated by the JSF Toolkit. The toolkit overrides
	 * {@link UIComponent#broadcast(FacesEvent)} and calls this method before
	 * super.broadcast.
	 * 
	 * @param component
	 *            the component that received the event
	 * 
	 * @param event
	 * 
	 * @throws AbortProcessingException
	 *             if the event should not be processed further.
	 */
	public void broadcast(UIComponent component, FacesEvent event)
			throws AbortProcessingException {
		// do nothing
	}
}
